package org.javahispano.javacup.tacticas_aceptadas.FGR.javacup2009.gallegux.javacup2009.canelaenrama;

import org.javahispano.javacup.tacticas_aceptadas.FGR.javacup2009.gallegux.javacup2009.canelaenrama.ed.IntDoubleDouble;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.Posicion;



public class TrayectoriaBalon 
{

	final static double G = Constantes.G;
	final static double FACTOR_DISMINUCION_VEL_SUELO = Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
	final static double FACTOR_DISMINUCION_VEL_AIRE = Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
	final static double FACTOR_DISMINUCION_ALTURA_REBOTE = Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
	final static double DISTANCIA_FIN_CALCULO = 0.003;
	final static double FUERZA_MAX_REMATE_JUGADOR = 1.0; // todos los jugadores tendran fuerza 1
	
	
	List<PosicionBalon> posiciones = new ArrayList<PosicionBalon>();
	
	//Logger log = Logger.getLogger(getClass().getName());
	
	Posicion origen = null;
	
	
	/**
	 * constructor para simular y calcular angulos y distancias
	 * el balon se movera solo en el eje X, el valor en el eje Y sera 0
	 * @param fuerza
	 * @param angulo angulo vertical
	 */
	public TrayectoriaBalon(double fuerzaRemate, double anguloVertical)
	{
		double velocidad = fuerzaRemate * Constantes.getVelocidadRemate(FUERZA_MAX_REMATE_JUGADOR);
		
		anguloVertical *= (Math.PI / 180d);
		//log.info("velocidad="+velocidad+" fuerza="+fuerzaRemate+" anguloVertical="+anguloVertical);
		velocidad *= Math.cos(anguloVertical);
		
		double pos2_z = redondeaMultiplo(velocidad * Math.sin(anguloVertical), G);
		
		posiciones = calcularTrayectoria(Constantes.centroCampoJuego, new Posicion(velocidad, 0), pos2_z);
		
		//print(20);
	}
	
	
	
	/**
	 * constructor para analizar la trayectoria del balon antes de tirar
	 * @param fuerzaRemate
	 * @param angVertical
	 * @param posOrigen
	 * @param posDestino
	 */
	public TrayectoriaBalon(double fuerzaRemate, double angVertical, Posicion posOrigen, Posicion posDestino)
	{
		double angHorizontal = posOrigen.angulo(posDestino);
		double velocidad = fuerzaRemate * Constantes.getVelocidadRemate(FUERZA_MAX_REMATE_JUGADOR);
		
		angVertical *= Math.PI /180d;
		
		if (velocidad != 0) { 
			// si el remate tiene velocidad
			velocidad *= Math.cos(angVertical);
			double balonDz = redondeaMultiplo(velocidad * Math.sin(angVertical), G);
			double balonDx = Math.cos(angHorizontal) * velocidad;
			double balonDy = Math.sin(angHorizontal) * velocidad;
			
			PosicionBalon pb2 = new PosicionBalon(balonDx, balonDy, balonDz);
			
			posiciones = calcularTrayectoria(posOrigen, pb2.getPosicion(), balonDz);
			posiciones.add(0, pb2);
		}
	}
	
	
	
	/**
	 * constructor para calcular la trayectoria a partir de dos puntos
	 * se utiliza cuando se detecta que la bola ha sido golpeada
	 * @param posicion1
	 * @param posicion2
	 * @param alturaPosicion2
	 */
	public TrayectoriaBalon(Posicion posicion1, Posicion posicion2, 
			double alturaPosicion2)
	{
		//log.info("pos1: " + posicion1 + "    pos2: " + posicion2 + "   alt: " + alturaPosicion2);
		
		posiciones = calcularTrayectoria(posicion1, posicion2, alturaPosicion2);
		
		//log.info("trayectoria de " + posiciones.size() + " posiciones");
		
//		if (posiciones.size() == 0) {
//			log.info(posicion1+" "+posicion2+" "+alturaPosicion2);
//		}
//		
//		print(10);
		
	}
	
	
	
	/**
	 * - el n� de iteraciones empieza por 1
	 * - posicion1 y posicion2 no se incluyen
	 * @param posicion1 es la posicion de golpeo, la altura del balon es 0
	 * @param posicion2 es el punto con el cual ya se puede calcular la trayectoria
	 * @param alturaPosicion2 la altura del balon en la pos2
	 * @param checkLimitesCampo tiene en cuenta si debe seguir calculando en caso de que el balon se
	 * salga del campo
	 * @return una lista de las siguientes posiciones empezando por la que seria
	 * posicion3
	 */
	private List<PosicionBalon> calcularTrayectoria(Posicion posicion1, Posicion posicion2, 
			double alturaPosicion2)
	{
		List<PosicionBalon> posiciones = new ArrayList<PosicionBalon>();
		double x, y, z; // coordenadas del balon
		double dx, dy, dz; // incrementos de la posicion del balon
		double absx; // abs(x)
		boolean suelo = false, fin = false;
		int nIteracion = 2; // n� iteracion de la posicion del balon
		// la primera posicion que se genera es la posicion2, pero no la guardamos
		x = posicion1.getX();
		y = posicion1.getY();
		z = 0.0;
		dx = posicion2.getX() - x;
		dy = posicion2.getY() - y;
		dz = alturaPosicion2;
		origen = posicion1;
		
//		log.info("1 = " + new PosicionBalon(x,y,0).toString());
//		log.info("2 = " + new PosicionBalon(x+dx,y+dy,dz).toString());

		while (!fin) {
			
			if (!suelo && z == 0 && dz >= 0 && dz < G * 3) {
				suelo = true;
			}
			
			if (suelo) {
				x += dx;
				y += dy;
				dx *= FACTOR_DISMINUCION_VEL_SUELO;
				dy *= FACTOR_DISMINUCION_VEL_SUELO;
			}
			else {
				z = redondeaMultiplo(z + dz, G);
				x += dx;
				y += dy;
				dz = redondeaMultiplo(dz - G, G);
				dx *= FACTOR_DISMINUCION_VEL_AIRE;
				dy *= FACTOR_DISMINUCION_VEL_AIRE;
				if (z == 0) {
					dz = (-dz - G) * FACTOR_DISMINUCION_ALTURA_REBOTE;
					dz = redondeaMultiplo(dz, G);
				}
			}
			
			if (nIteracion > 2) {
				posiciones.add(new PosicionBalon(x,y,z));
				//log.info(new PosicionBalon(nIteracion, x,y,z).toString());
			}
			
			nIteracion++;
			
			fin = (Math.abs(dx) < DISTANCIA_FIN_CALCULO && Math.abs(dy) < DISTANCIA_FIN_CALCULO);// || !Util.estaEnCampo(x,y);
			
			// tratamiento de postes
	        if (Math.abs(y) > CTE.MITAD_LARGO_CAMPO ) { // si sobrepasa la linea de fondo
	        	if (z <= Constantes.ALTO_ARCO) { // si la altura es menor que el larguero
	        		absx = Math.abs(x);
	        		if (absx < CTE.MITAD_LARGUERO - Constantes.RADIO_BALON) { // es gol?
	        			if (y < 0) { // gol del rival, lo debemos parar
	        				// recuperamos la ultima posicion introducida y le ponemos gol=true
	        				if (posiciones.size() > 0) {
		        				posiciones.get(posiciones.size()-1).setGolContra(true);
	        				}
	        				fin = true;
	        			} 
	        			else { // gol nuestro, tal vez lo paren
	        				// recuperamos la ultima posicion introducida y le ponemos gol=true
	        				if (posiciones.size() > 0) {
	        					posiciones.get(posiciones.size()-1).setGolFavor(true);
	        				}
	        				fin = true;
	        				// como es gol acaba la trayectoria, si lo paran cambiara
	        				// la trayectoria y se genera de nuevo
	        			}
	        		}
	        		if (Util.estaEn(absx, CTE.DIST_ABS_POSTE_INTERIOR, CTE.DIST_ABS_POSTE_EXTERIOR)) {
	        			// da en un poste
	        			Posicion poste = null; // posicion donde rebota
	        			Posicion reb = null;
	        			if (y < 0) { // nuesstro campo
	        				if (x < 0) {
	        					poste = Constantes.posteIzqArcoSup;
	        				} 
	        				else {
	        					poste = Constantes.posteDerArcoSup;
	        				}
	        			} 
	        			else { // campo contrario
	        				if (x < 0) {
	        					poste = Constantes.posteIzqArcoInf;
	        				} 
	        				else {
	        					poste = Constantes.posteDerArcoInf;
	        				}
	        			}
						reb = new Posicion(x - poste.getX(), poste.getY() - y);
						double ang = reb.angulo();
						double vel = Math.sqrt(dx * dx + dy * dy) * .8;
						dx = Math.cos(ang) * vel;
						dy = Math.sin(ang) * vel;
	        		}
	        	}
	        }
		}
		
		return posiciones;
	}
	
	
		
    private double redondeaMultiplo(double valor, double divisor) {
        return Math.round(valor / divisor) * divisor;
    }

    
    
    public List<PosicionBalon> getPosiciones()
    {
    	return posiciones;
    }
    
    
    /**
     * mira si el tiro en algun momento supera la altura
     * @param altura
     * @return
     */
    public boolean isTiroAlto(double altura)
    {
    	boolean alto = false;
    	
    	for (int i = 0; !alto && i < posiciones.size(); i++) {
    		alto = posiciones.get(i).getZ() > altura; 
    	}
    	
    	return alto;
    }
    
    
    
    /**
     * calcula en que iteracion cae el balon al suelo
     * pensado para precalculos
     * int=iteraciones
     * double1=distancia antes de caer al suelo
     * double2=distancia en el suelo
     * @return
     */
    public IntDoubleDouble buscarIteracionSuelo()
    {
    	PosicionBalon pb = null;
    	
    	for (int i = 0; i < posiciones.size(); i++) {
    		pb = posiciones.get(i);
    		if (pb.getZ() > 3) { // altura larguero - radio balon
    			return null;
    		}
    		if (pb.getZ() == 0) {
    			if (i == 0) {
    				return null;
    			}
    			else {
    				return new IntDoubleDouble(i, posiciones.get(i-1).getX(), pb.getX());
    			}
    		}
    	}
    	
    	return null;
    }
    
    
    
    public boolean hasGolContra()
    {
    	int posicionesSize = posiciones.size();
    	
    	return (posicionesSize > 0) ? posiciones.get(posicionesSize - 1).isGolContra() : false;
    }
    
    
    public boolean hasGolFavor()
    {
    	int posicionesSize = posiciones.size();
    	
    	return (posicionesSize > 0) ? posiciones.get(posicionesSize - 1).isGolFavor() : false;
    }
    
    
//    public void print(int max)
//    {
//    	if (max <= 0) max = 1000;
//    	
//		for (int i = 0; i < posiciones.size() && i < max; i++) {
//			log.info
//			//System.out.println
//				(posiciones.get(i).toString() + " " + posiciones.get(i).getPosicion().distancia(origen));
//		}
//    }
//    
//    
//    public void printS(int max)
//    {
//    	if (max <= 0) max = 1000;
//    	
//    	System.out.println("--------------------");
//		for (int i = 0; i < posiciones.size() && i < max; i++) {
//			//log.info
//			System.out.println
//				(posiciones.get(i).toStringPosicion() + " " + Util.toString(posiciones.get(i).getPosicion().distancia(new Posicion(0,0))));
//		}
//    }
    
    //-----------------------------------
    
    
//    public static void main(String...args)
//    {
//    	try {
//    		TrayectoriaBalon tb = null;
//    		
////    		tb = new TrayectoriaBalon(
////    				new Posicion(0 , 0), 
////    				//new Posicion(1.8000000000000003 , 0.0) , 1.08);
////    				//new Posicion(1.2727922 , 1.2727922) , 1.08);
////    				new Posicion(1.4400000000000002 , 0.0) , 0.84);
////    		
////    		tb.print(4);
//    		
//    		tb = new TrayectoriaBalon(1.0, 22, new Posicion(0,0), new Posicion(100,0));
//    		tb.printS(-1);
//
//    		tb = new TrayectoriaBalon(1.0, 28, new Posicion(0,0), new Posicion(45,55));
//    		tb.printS(-1);
//
//    	}
//    	catch (Exception e) {
//    		e.printStackTrace();
//    	}
//    }
    
    
	
}
